package com.ruoyi.business.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.zxing.WriterException;
import com.ruoyi.business.domain.TRegisterSign;
import com.ruoyi.business.domain.TUserOptLog;
import com.ruoyi.business.enums.SignShortEnums;
import com.ruoyi.business.service.*;
import com.ruoyi.business.utils.PDF7Utils;
import com.ruoyi.business.vo.*;
import com.ruoyi.business.websocket.WebSocketController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.constant.RoleConstants;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.system.service.ISysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 签署告知书信息Controller
 *
 * @author Tellsea
 * @date 2025-03-25
 */
@Api(tags = "签署告知书信息Controller")
@RestController
@RequestMapping("/business/registerSign")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class TRegisterSignController extends BaseController {

    private final ITRegisterSignService tRegisterSignService;

    private final ITRegisterNoticeService registerNoticeService;

    private final ITRegisterInfoService registerInfoService;

    private final ISysUserSignService sysUserSignService;

    private final ISysUserService sysUserService;

    private final ITUserOptLogService tUserOptLogService;

    private final ITRegisterSignSmsService tRegisterSignSmsService;

    private final WebSocketController webSocketController;

    @Autowired
    private RedisCache redisCache;

    private final static Map<String, String> PDF_TEMPLATE_CACHE = new HashMap<>();

    static {
        String projectSignPath = System.getProperty("user.dir") + "/uploadPath";
        String signGZSTemplate = projectSignPath + "/sign/template/华山医院进修生告知书.pdf";
//        String GLSignGZSTemplate = projectSignPath + "/sign/template/行风自律承诺书-管理.pdf";
        String HSSignGZSTemplate = projectSignPath + "/sign/template/行风自律承诺书-护士.pdf";
        String YSSignGZSTemplate = projectSignPath + "/sign/template/行风自律承诺书-医生.pdf";
        PDF_TEMPLATE_CACHE.put(SignShortEnums.JXS_GZS.name(), signGZSTemplate);
        PDF_TEMPLATE_CACHE.put(SignShortEnums.HS_CNS.name(), HSSignGZSTemplate);
//        PDF_TEMPLATE_CACHE.put(SignShortEnums.GZRY_CNS.name(), GLSignGZSTemplate);
        PDF_TEMPLATE_CACHE.put(SignShortEnums.KZR_CNS.name(), YSSignGZSTemplate);
    }

    @ApiOperation("查询签署告知书信息列表")
    @PreAuthorize("@ss.hasPermi('business:registerSign:list')")
    @GetMapping("/list")
    public TableDataInfo<TRegisterSignVo> list(TRegisterSignVo entity) {
        return tRegisterSignService.queryList(entity);
    }

    @ApiOperation("查询签署告知书信息所有列表")
    @GetMapping("/listAll")
    public AjaxResult listAll(TRegisterSignVo entity) {
        return AjaxResult.success("查询成功", tRegisterSignService.queryAll(entity));
    }

    @ApiOperation("导出签署告知书信息列表")
    @PreAuthorize("@ss.hasPermi('business:registerSign:export')")
    @Log(title = "签署告知书信息", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, TRegisterSignVo entity) {
        List<TRegisterSignVo> list = tRegisterSignService.queryAll(entity);
        ExcelUtil<TRegisterSignVo> util = new ExcelUtil<>(TRegisterSignVo.class);
        util.exportExcel(response, list, "签署告知书信息数据");
    }

    @ApiOperation("获取签署告知书信息详细信息")
    @PreAuthorize("@ss.hasPermi('business:registerSign:query')")
    @GetMapping(value = "/getInfo/{uuid}")
    public AjaxResult getInfo(@PathVariable("uuid") String uuid) {
        return AjaxResult.success("查询成功", tRegisterSignService.queryById(uuid));
    }

    @ApiOperation("获取已签名的详细信息")
    @GetMapping(value = "/getSignedInfo/{registerId}")
    public AjaxResult getSignedInfo(@PathVariable("registerId") String registerId) {
        TRegisterSignVo entity = new TRegisterSignVo();
        entity.setRegisterId(Long.valueOf(registerId));
        entity.setHasSigned(true);
        return AjaxResult.success("查询成功", tRegisterSignService.queryAll(entity));
    }

    @ApiOperation("新增签署告知书信息")
    @PreAuthorize("@ss.hasPermi('business:registerSign:add')")
    @Log(title = "签署告知书信息", businessType = BusinessType.INSERT)
    @PostMapping("add")
    public AjaxResult add(@RequestBody TRegisterSignVo entity) {
        return toAjax(tRegisterSignService.save(entity));
    }

    @ApiOperation("修改签署告知书信息")
    @PreAuthorize("@ss.hasPermi('business:registerSign:edit')")
    @Log(title = "签署告知书信息", businessType = BusinessType.UPDATE)
    @PostMapping("edit")
    public AjaxResult edit(@RequestBody TRegisterSignVo entity) {
        return toAjax(tRegisterSignService.updateById(entity));
    }

    @ApiOperation("删除签署告知书信息")
    @PreAuthorize("@ss.hasPermi('business:registerSign:remove')")
    @Log(title = "签署告知书信息", businessType = BusinessType.DELETE)
    @GetMapping("/remove/{uuids}")
    public AjaxResult remove(@PathVariable String[] uuids) {
        return toAjax(tRegisterSignService.removeByIds(Arrays.asList(uuids)) ? 1 : 0);
    }

    @ApiOperation("查询需要签字的学员id")
    @PostMapping("/isNeedSign")
    public AjaxResult isNeedSign(@RequestBody TRegisterUserSign query) {
        if(query.getUserId()==null){
            return AjaxResult.error("进修科室主任的用户id必传，userId");
        }
        List<TRegisterSignSendSmsVo> t = tRegisterSignSmsService.getKXBatchNoSignByUserId(query.getUserId());
        if(t==null){
            return AjaxResult.success(Collections.emptyList());
        }
        List<Long> regIds = t.stream().map(TRegisterSign::getRegisterId).collect(Collectors.toList());
        return AjaxResult.success(regIds);
    }

    @ApiOperation("科室/护士长批量签字")
    @PostMapping("addKXBatchSign")
    public AjaxResult addKXBatchSign(@RequestBody TRegisterUserSign query) {
        List<TRegisterSign> registerSignList = tRegisterSignService.getKXBatchNoSign(query);
        Date nowDate = DateUtils.getNowDate();
        SysUser user = sysUserService.selectUserByUserName(query.getUserName());
        List<String> roleList = new ArrayList<>();
        if(user.getRoles()!=null){
            for (SysRole role : user.getRoles()) {
                roleList.add(role.getRoleName());
            }
        }
        for (TRegisterSign entity : registerSignList) {
            entity.setUpdateTime(nowDate);
            entity.setUpdateBy(query.getUserName());
            if(!query.getUserSignImg().startsWith(PDF7Utils.IMAGE_PNG_BASE_64)){
                query.setUserSignImg(PDF7Utils.IMAGE_PNG_BASE_64+query.getUserSignImg());
            }
            entity.setOtherSignImg(query.getUserSignImg());
            String date = DateUtils.getDate();
            entity.setOtherSignDate(date);
            String signPdfPDF = procSignPdfPDF(entity);
            entity.setSignPdfFile(signPdfPDF);
            QueryWrapper<TRegisterSign> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("uuid",entity.getUuid());
            entity.setSignOtherStatus(1);
            tRegisterSignService.update(entity,queryWrapper);
            // 记录日志
            TUserOptLog tUserOptLog = new TUserOptLog()
                    .setOptDesc("科主任签署行风承诺书")
                    .setOptTime(nowDate)
                    .setFromUserid(user.getUserId().toString())
                    .setFromUsername(user.getNickName())
                    .setFromRolename(StringUtils.join(roleList,","))
                    .setToUserid(entity.getRegisterId().toString());
            tUserOptLogService.save(tUserOptLog);
        }
        if(StringUtils.isNotEmpty(query.getExpireUuid())){
            redisCache.setCacheObject(query.getExpireUuid(),query.getUserSignImg(),20, TimeUnit.SECONDS);
            webSocketController.OnMessage(query.getExpireUuid(),query.getUserSignImg());
        }
        return AjaxResult.success("签名完成");
    }

    @ApiOperation("获取二维码签字照片")
    @GetMapping(value = "/getUserWriteSign/{expireUuid}")
    public AjaxResult getUserWriteSign(@PathVariable("expireUuid") String expireUuid) {
        if(!redisCache.hasKey(expireUuid)){
            return AjaxResult.error("二维码已失效，请重新获取");
        }
        String result = redisCache.getCacheObject(expireUuid).toString();
        if(StringUtils.equals(result,"0")){
           return AjaxResult.success("待扫描签字");
        }
        return AjaxResult.success("签字成功", result);
    }

    @ApiOperation("给签名文件签字")
    @PostMapping("addSignBook")
    public AjaxResult addSignBook(@RequestBody TRegisterUserSign query) {
        String expireUuid = query.getExpireUuid();
        if(StringUtils.isEmpty(expireUuid) || !redisCache.hasKey(expireUuid)){
            throw new BaseException("扫描二维码已过期");
        }
        TRegisterSignVo entity = tRegisterSignService.queryById(query.getGuid());
        Date nowDate = DateUtils.getNowDate();
        entity.setUpdateTime(nowDate);
        entity.setUpdateBy(query.getUserName());
        if(!query.getUserSignImg().startsWith(PDF7Utils.IMAGE_PNG_BASE_64)){
            query.setUserSignImg(PDF7Utils.IMAGE_PNG_BASE_64+query.getUserSignImg());
        }
        // 如果签名已完成那么直接返回成功
        if(entity.getSignType()==1 && entity.getSignSelfStatus()==1){
            // 只需要1级签名，并且自己签名已完成
            return AjaxResult.error("签名已存在",entity);
        }
        if(entity.getSignType()==2 && entity.getSignSelfStatus()==1 && entity.getSignOtherStatus()==1){
            // 只需要2级签名，并且自己签名以及医院方已完成
            return AjaxResult.error("签名已存在",entity);
        }
        String date = DateUtils.getDate();
        if(query.getStudentRole()){
            // 学员角色，此时是学员签字
            entity.setSignSelfStatus(1);
            entity.setSelfSignImg(query.getUserSignImg());
            entity.setSelfSignDate(date);
        }else{
            entity.setSignOtherStatus(1);
            entity.setOtherSignImg(query.getUserSignImg());
            entity.setOtherSignDate(date);
        }
        String signPdfPDF = procSignPdfPDF(entity);
        entity.setSignPdfFile(signPdfPDF);
        QueryWrapper<TRegisterSign> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("uuid",query.getGuid());
        tRegisterSignService.update(entity,queryWrapper);
        redisCache.setCacheObject(expireUuid,query.getUserSignImg(),10, TimeUnit.SECONDS);
        webSocketController.OnMessage(expireUuid,query.getUserSignImg());
        // 记录日志
        SysUser user = sysUserService.selectUserByUserName(query.getUserName());
        List<String> roleList = new ArrayList<>();
        if(user.getRoles()!=null){
            for (SysRole role : user.getRoles()) {
                roleList.add(role.getRoleName());
            }
        }
        TUserOptLog tUserOptLog = new TUserOptLog()
                .setOptDesc(entity.getSignPdfName())
                .setOptTime(nowDate)
                .setFromUserid(user.getUserId().toString())
                .setFromUsername(user.getNickName())
                .setFromRolename(StringUtils.join(roleList,","))
                .setToUserid(entity.getRegisterId().toString());
        tUserOptLogService.save(tUserOptLog);
        return AjaxResult.success("签名完成",entity);
    }

    @ApiOperation("科室批量签字生成二维码")
    @PostMapping("getKSBatchQRCode")
    public AjaxResult getKSBatchQRCode(@RequestBody TRegisterSignVo entity) throws IOException, WriterException {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        List<Long> registerIdList = entity.getRegisterIdList();
        String registerIds = StringUtils.join(registerIdList,",");
        String signerRole = entity.getSignShort();
        if(StringUtils.isEmpty(signerRole)){
            if (SecurityUtils.hasRole(RoleConstants.DEPT_MANAGE.getRoleKey())) {
                List<Long> registerDeptIdList = user.getRegisterDeptIdList();
                if(registerDeptIdList!=null && registerDeptIdList.contains(13L)){
                    // 如果包含了护理那么就是护士长
                    signerRole = SignShortEnums.HS_CNS.name();
                }else{
                    signerRole = SignShortEnums.KZR_CNS.name();
                }
            }
        }
        if(StringUtils.isEmpty(signerRole)){
            throw new BaseException("该角色不允许批量签字");
        }
        String redisKey = Md5Utils.hash(registerIds+System.currentTimeMillis());
        // 增加二维码参数key
        String timeParams = "&expireUuid="+redisKey;
        String urlAppend = "registerIds="+registerIds+"&userName="+user.getUserName()+"&signerRole="+signerRole+timeParams;
        urlAppend = URLEncoder.encode(urlAppend, "UTF-8");
        String url = entity.getWebTargetUrl().contains("?")?(entity.getWebTargetUrl()+ "&"+ urlAppend):(entity.getWebTargetUrl()+"?" + urlAppend);
        String qrCodeImage = PDF7Utils.generateQRCodeImage(url, 300, 300);
        SysUserSignVo sysUserSignVo = sysUserSignService.queryById(user.getUserId());
        String mySignQR = "";
        if(sysUserSignVo!=null){
            mySignQR = sysUserSignVo.getUserSign();
        }
        redisCache.setCacheObject(redisKey,"0", WebSocketController.sessionTimeout, TimeUnit.MILLISECONDS);
        Map<String,Object> result = new HashMap<>();
        result.put("mySignQR",mySignQR);
        result.put("qrCodeImage",qrCodeImage);
        result.put("scanUrl",url);
        result.put("expireUuid",redisKey);
        return AjaxResult.success("二维码创建成功",result);
    }


    @ApiOperation("获取签名文件信息")
    @PostMapping("getSignBook")
    public AjaxResult getSignBook(@RequestBody TRegisterSignVo entity) throws IOException, WriterException {
        TRegisterNoticeVo registerNoticeVo = registerNoticeService.queryByRegisterId(entity.getRegisterId());
        if(registerNoticeVo==null){
            throw new BaseException("该学员未发送通知书");
        }
        TRegisterInfoVo tRegisterInfoVo = registerInfoService.queryById(entity.getRegisterId());
        switch (entity.getSignBookKind()){
            case 1:
                entity.setSignShort(SignShortEnums.JXS_GZS.name());
                break;
            case 2:
                if(tRegisterInfoVo.getRegisterDeptId().equals(13L)){
                    entity.setSignShort(SignShortEnums.HS_CNS.name());
                }else{
                    entity.setSignShort(SignShortEnums.KZR_CNS.name());
                }
                break;
            default:
                throw new BaseException("不支持该类型通知书签名");
        }

        TRegisterSign signByKind = tRegisterSignService.getSignByKind(entity);
        SysUser user = SecurityUtils.getLoginUser().getUser();
        if (signByKind == null) {
            signByKind = initRegisterSign(entity, user);
        }
        TRegisterSignResult result = new TRegisterSignResult();
        result.setStudentSignImg(signByKind.getSelfSignImg());
        // 如果签名已完成那么不返回二维码
        if(signByKind.getSignType()==1 && signByKind.getSignSelfStatus()==1){
            // 只需要1级签名，并且自己签名已完成
            result.setSignStatus(1);
            result.setSignFile(signByKind.getSignPdfFile());
            return AjaxResult.success("签名已完成",result);
        }
        if(signByKind.getSignType()==2 && signByKind.getSignSelfStatus()==1 && signByKind.getSignOtherStatus()==1){
            // 只需要2级签名，并且自己签名以及医院方已完成
            result.setSignStatus(1);
            result.setSignFile(signByKind.getSignPdfFile());
            return AjaxResult.success("签名已完成",result);
        }
        // 判断当前签字人是学员还是医院方
        boolean isStudent = SecurityUtils.hasRole(RoleConstants.STUDENT.getRoleKey());
        switch (signByKind.getSignType()){
            case 1:
                // 当只需要学员方签字时;不是学员角色的不能签字
                if (!isStudent && (signByKind.getSignSelfStatus()==0)) {
                    result.setSignStatus(0);
                    result.setSignFile(signByKind.getSignPdfFile());
                    return AjaxResult.success("待学员登录签字",result);
                }
                break;
            case 2:
                // 当需要双方签字时
                // 当只需要学员方签字时;不是学员角色的不能签字
                if (!isStudent && (signByKind.getSignSelfStatus()==0)) {
                    result.setSignStatus(0);
                    result.setSignFile(signByKind.getSignPdfFile());
                    return AjaxResult.success("待学员登录签字",result);
                }
                // 当只需要医院方签字时;不是医院角色的不能签字
                if (isStudent && signByKind.getSignSelfStatus()==1 && signByKind.getSignOtherStatus()==0) {
                    result.setSignStatus(0);
                    result.setSignFile(signByKind.getSignPdfFile());
                    return AjaxResult.success("待医院登录签字",result);
                }
                break;
            default:
                throw new BaseException("不支持该类型通知书签名");
        }

        if(isStudent && !Objects.equals(tRegisterInfoVo.getUserId(), user.getUserId())){
            return AjaxResult.error("非本人不能签字");
        }
        String redisKey = Md5Utils.hash(signByKind.getUuid()+System.currentTimeMillis());
        // 增加二维码参数key
        String timeParams = "&expireUuid="+redisKey;
        String urlAppend = "guid="+signByKind.getUuid()+"&studentRole="+isStudent+"&userName="+user.getUserName()+timeParams;
        urlAppend = URLEncoder.encode(urlAppend, "UTF-8");
        String url = entity.getWebTargetUrl().contains("?")?(entity.getWebTargetUrl()+ "&"+ urlAppend):(entity.getWebTargetUrl()+"?" + urlAppend);
        String qrCodeImage = PDF7Utils.generateQRCodeImage(url, 300, 300);
        result.setQrCodeImage(qrCodeImage);
        // 设置redis缓存 2分钟后key过期
        redisCache.setCacheObject(redisKey,"0",  WebSocketController.sessionTimeout, TimeUnit.MILLISECONDS);
        result.setSignStatus(0);
        result.setSignFile(signByKind.getSignPdfFile());
        result.setExpireUuid(redisKey);
        result.setScanUrl(url);
        return AjaxResult.success(result);
    }


    /**
     * 初始化签名信息
     * @param entity
     * @param user
     * @return
     */
    private TRegisterSign initRegisterSign(@RequestBody TRegisterSignVo entity, SysUser user) {
        Date nowDate = DateUtils.getNowDate();
        String uuid = Md5Utils.hash(entity.getRegisterId() + "\t" + entity.getSignShort());
        entity.setUuid(uuid);
        String pdfFileName = entity.getSignPdfName() == null ? SignShortEnums.getSignNameByCode(entity.getSignShort()) : entity.getSignPdfName();
        if(StringUtils.isEmpty(pdfFileName)){
            throw new BaseException("解析不出告知书名称，不支持类型告知书");
        }
        entity.setSignPdfName(pdfFileName);
        entity.setSignOtherStatus(0);
        entity.setSignSelfStatus(0);
        int signType=entity.getSignShort().equals(SignShortEnums.JXS_GZS.name())?1:2;
        entity.setSignType(signType);
        entity.setCreateTime(nowDate);
        entity.setUpdateTime(nowDate);
        entity.setCreateBy(user.getUserName());
        entity.setUpdateBy(user.getUserName());
        String signPdfPDF = procSignPdfPDF(entity);
        entity.setSignPdfFile(signPdfPDF);
        tRegisterSignService.save(entity);
        return tRegisterSignService.getSignByKind(entity);
    }

    public String procSignPdfPDF(TRegisterSign registerSign) {
        String pdfTemplate = PDF_TEMPLATE_CACHE.get(registerSign.getSignShort());
        if(StringUtils.isEmpty(pdfTemplate)){
            throw new BaseException("不支持类型告知书");
        }
        String pdfFileName = registerSign.getSignPdfName() == null ? SignShortEnums.getSignNameByCode(registerSign.getSignShort()) : registerSign.getSignPdfName();
        if(StringUtils.isEmpty(pdfFileName)){
            throw new BaseException("解析不出告知书名称，不支持类型告知书");
        }
        TRegisterNoticeVo noticeVo = registerNoticeService.queryByRegisterId(registerSign.getRegisterId());
        if(noticeVo.getRegisterDate()==null){
            throw new BaseException("报道日期为空，请核实报名数据");
        }
        Map<String,String> imgMap = new HashMap<>();
        Map<String,String> txtMap = new HashMap<>();
        TRegisterInfoVo registerInfoVo = registerInfoService.queryById(registerSign.getRegisterId());
        String outPdfName = pdfFileName+"-"+registerInfoVo.getUserName()+"-"+registerSign.getRegisterId()+".pdf";
        // 填充告知书初始化模版
        String[] startDateArr = noticeVo.getRegisterDate().contains("/")?noticeVo.getRegisterDate().split("/"):noticeVo.getRegisterDate().split("-");
        txtMap.put("deptName",registerInfoVo.getRegisterDeptName());
        txtMap.put("sureYear",startDateArr[0]);
        txtMap.put("sureMonth",startDateArr[1]);
        txtMap.put("sureDate",startDateArr[2]);
        // 填充签字数据
        String selfSignDate = registerSign.getSelfSignDate();
        String selfSignImg = registerSign.getSelfSignImg();
        String otherSignDate = registerSign.getOtherSignDate();
        String otherSignImg = registerSign.getOtherSignImg();
        if(StringUtils.isNotEmpty(selfSignDate)){
            String[] dateArr = selfSignDate.split("-");
            txtMap.put("selfCurrentYear",dateArr[0]);
            txtMap.put("selfCurrentMonth",dateArr[1]);
            txtMap.put("selfCurrentDate",dateArr[2]);
        }
        if(StringUtils.isNotEmpty(selfSignImg)){
            imgMap.put("selfSign",selfSignImg);
        }
        if(StringUtils.isNotEmpty(otherSignDate)){
            String[] dateArr = otherSignDate.split("-");
            txtMap.put("otherCurrentYear",dateArr[0]);
            txtMap.put("otherCurrentMonth",dateArr[1]);
            txtMap.put("otherCurrentDate",dateArr[2]);
        }
        if(StringUtils.isNotEmpty(otherSignImg)){
            imgMap.put("otherSign", otherSignImg);
        }

        String projectSignPath = System.getProperty("user.dir") + "/uploadPath";
        String signOutTemplate = "/sign/files/"+outPdfName;
        PDF7Utils.fillPdfTemplate(pdfTemplate,projectSignPath + signOutTemplate,txtMap,imgMap);
        return "/profile"+signOutTemplate;
    }

}
